
package com.hexinfo.dmpro.dev.data.transfer.service.impl;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hexinfo.dmpro.common.base.PageResult;
import com.hexinfo.dmpro.common.utils.CommonConstants;
import com.hexinfo.dmpro.data.transfer.common.model.CdhColumnsEntity;
import com.hexinfo.dmpro.data.transfer.common.service.ICdhMetadataService;
import com.hexinfo.dmpro.dev.data.transfer.common.utils.FieldListHandler;
import com.hexinfo.dmpro.dev.data.transfer.common.utils.FileTransferUtil;
import com.hexinfo.dmpro.dev.data.transfer.dao.DesensitizeFieldListMapper;
import com.hexinfo.dmpro.dev.data.transfer.model.DesensitizeFieldFilter;
import com.hexinfo.dmpro.dev.data.transfer.model.DesensitizeFieldList;
import com.hexinfo.dmpro.dev.data.transfer.model.DesensitizeFieldMap;
import com.hexinfo.dmpro.dev.data.transfer.model.dto.FieldListDto;
import com.hexinfo.dmpro.dev.data.transfer.service.IDesensitizeFieldFilterService;
import com.hexinfo.dmpro.dev.data.transfer.service.IDesensitizeFieldListService;
import com.hexinfo.dmpro.dev.data.transfer.service.IDesensitizeFieldMapService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 脱敏字段配置表
 *
 * @author yyh.huang
 * @date 2023-09-19 19:04:23
 */
@Service
public class DesensitizeFieldListServiceImpl extends ServiceImpl<DesensitizeFieldListMapper, DesensitizeFieldList> implements IDesensitizeFieldListService {

    @Resource
    private FieldListHandler fieldListHandler;

    @Autowired
    private ICdhMetadataService cdhMetadataService;

    @Autowired
    private IDesensitizeFieldMapService desensitizeFieldMapService;

    @Autowired
    private IDesensitizeFieldFilterService desensitizeFieldFilterService;

    @Value("${file.excelTemp}")
    String tempPath;

    @Override
    public PageResult queryList(Page page, DesensitizeFieldList desensitizeFieldList) {
        LambdaQueryWrapper<DesensitizeFieldList> wrapper = beforeQuery(desensitizeFieldList);
        Page returnPage = this.baseMapper.selectPage(page, wrapper);
        return new PageResult(returnPage.getRecords(), returnPage.getTotal());
    }

    /**
     * @Method beforeQuery
     * @Param
     * @param desensitizeFieldList
     * @Return com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<com.hexinfo.dmpro.dev.data.transfer.model.DesensitizeFieldList>
     * @Description 搜索前置条件
     * @Author Wang zhihao
     * @Date 2024/1/5 14:16
     * @Version V1.0
     */
    private LambdaQueryWrapper<DesensitizeFieldList> beforeQuery(DesensitizeFieldList desensitizeFieldList) {
        LambdaQueryWrapper<DesensitizeFieldList> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(DesensitizeFieldList::getDel, CommonConstants.STATUS_NORMAL);
        if (!StrUtil.isEmpty(desensitizeFieldList.getDesensitizeFunction())) {
            wrapper.like(DesensitizeFieldList::getDesensitizeFunction, desensitizeFieldList.getDesensitizeFunction());
        }
        if (!StrUtil.isEmpty(desensitizeFieldList.getFieldName())) {
            wrapper.like(DesensitizeFieldList::getFieldName, desensitizeFieldList.getFieldName());
        }
        if (!StrUtil.isEmpty(desensitizeFieldList.getTableName())) {
            wrapper.like(DesensitizeFieldList::getTableName, desensitizeFieldList.getTableName());
        }
        if (!StrUtil.isEmpty(desensitizeFieldList.getDbName())) {
            wrapper.like(DesensitizeFieldList::getDbName, desensitizeFieldList.getDbName());
        }
        return wrapper;
    }

    /**
     * @Method queryListNoPage
     * @Param
     * @Return java.util.List<com.hexinfo.dmpro.dev.data.transfer.model.DesensitizeFieldList>
     * @Description 不分页获取所有数据
     * @Author Wang zhihao
     * @Date 2024/1/2 14:37
     * @Version V1.0
     */
    @Override
    public List<DesensitizeFieldList> queryListNoPage() {
        LambdaQueryWrapper<DesensitizeFieldList> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(DesensitizeFieldList::getDel, CommonConstants.STATUS_NORMAL);
        return this.baseMapper.selectList(wrapper);
    }

    /**
     * @Method queryListNoPage
     * @Param
     * @param desensitizeFieldList
     * @Return java.util.List<com.hexinfo.dmpro.dev.data.transfer.model.DesensitizeFieldList>
     * @Description 根据条件不分页获取数据
     * @Author Wang zhihao
     * @Date 2024/1/5 14:12
     * @Version V1.0
     */
    @Override
    public List<DesensitizeFieldList> queryListNoPage(DesensitizeFieldList desensitizeFieldList) {
        LambdaQueryWrapper<DesensitizeFieldList> wrapper = beforeQuery(desensitizeFieldList);
        return this.baseMapper.selectList(wrapper);
    }

    @Override
    public List<DesensitizeFieldList> queryDesensitizeField(String dbName, String tableName) {
        LambdaQueryWrapper<DesensitizeFieldList> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(DesensitizeFieldList::getDel, CommonConstants.STATUS_NORMAL)
                .eq(DesensitizeFieldList::getDbName, dbName)
                .eq(DesensitizeFieldList::getTableName, tableName);
        return this.baseMapper.selectList(wrapper);
    }

    public boolean importFromExcel(MultipartFile file) {
        // 1.检查导入的文件
        checkFile(file);
        // 2.保存文件到服务器本地
        File backupFile = backupUploadFile(file);
        // 2.读取EXCEL文件数据，并做校验
        List<FieldListDto> excelData = getExcelData(backupFile);
        // 3.保存数据
        updateDbDataByExcel(excelData);
        return true;
    }

    @Override
    @Transactional
    public void create(DesensitizeFieldList desensitizeFieldList) {
        // 根据字段获取源数据库中符合的库表信息
        List<CdhColumnsEntity> cdhColumnsEntities = cdhMetadataService.queryTableByFieldName(desensitizeFieldList.getFieldName());
        if (CollectionUtil.isEmpty(cdhColumnsEntities)) {
            throw new RuntimeException("该字段没有找到对应的表");
        }
        // 添加脱敏字段映射
        addDesensitizeFieldMap(desensitizeFieldList);
        // 过滤掉黑名单中的脱敏字段
        cdhColumnsEntities = filterField(cdhColumnsEntities);
        // 添加脱敏字段
        addDesensitizeFieldList(desensitizeFieldList, cdhColumnsEntities);
    }

    private void addDesensitizeFieldList(DesensitizeFieldList desensitizeFieldList, List<CdhColumnsEntity> cdhColumnsEntities) {
        for (CdhColumnsEntity cdhColumnsEntity : cdhColumnsEntities) {
            if (!isExist(desensitizeFieldList, cdhColumnsEntity)){
                DesensitizeFieldList newDesensitizeFieldList = new DesensitizeFieldList();
                newDesensitizeFieldList.setFunctionId(desensitizeFieldList.getFunctionId());
                newDesensitizeFieldList.setDbName(cdhColumnsEntity.getDbName());
                newDesensitizeFieldList.setTableName(cdhColumnsEntity.getTableName());
                newDesensitizeFieldList.setFieldName(desensitizeFieldList.getFieldName());
                newDesensitizeFieldList.setDesensitizeFunction(desensitizeFieldList.getDesensitizeFunction());
                this.baseMapper.insert(newDesensitizeFieldList);
            }
        }
    }

    /**
     * @Method addDesensitizeFieldMap
     * @Param
     * @param desensitizeFieldList
     * @Return void
     * @Description 新增字段映射
     * @Author Wang zhihao
     * @Date 2024/1/5 13:40
     * @Version V1.0
     */
    private void addDesensitizeFieldMap(DesensitizeFieldList desensitizeFieldList) {
        if (!isExistMap(desensitizeFieldList)) {
            DesensitizeFieldMap desensitizeFieldMap = new DesensitizeFieldMap()
                    .setFunctionId(desensitizeFieldList.getFunctionId())
                    .setDesensitizeField(desensitizeFieldList.getFieldName())
                    .setDesensitizeExample(desensitizeFieldList.getDesensitizeFunction());
            desensitizeFieldMapService.save(desensitizeFieldMap);
        }
    }

    /**
     * @Method isExistMap
     * @Param
     * @param desensitizeFieldList
     * @Return boolean
     * @Description 检验映射是否存在
     * @Author Wang zhihao
     * @Date 2024/1/5 13:41
     * @Version V1.0
     */
    private boolean isExistMap(DesensitizeFieldList desensitizeFieldList) {
        List<DesensitizeFieldMap> desensitizeFieldMapList = desensitizeFieldMapService.queryListNoPage(
                new DesensitizeFieldMap().setDesensitizeField(desensitizeFieldList.getFieldName())
        );
        return CollectionUtil.isNotEmpty(desensitizeFieldMapList);
    }

    /**
     * @Method reset
     * @Param
     * @Return void
     * @Description 刷新脱敏字段配置表
     * @Author Wang zhihao
     * @Date 2024/1/5 15:55
     * @Version V1.0
     */
    @Override
    public void reset() {
        // 获取所有字段映射
        List<DesensitizeFieldMap> desensitizeFieldMapList = desensitizeFieldMapService.queryListNoPage(new DesensitizeFieldMap());
        // 遍历映射
        for (DesensitizeFieldMap desensitizeFieldMap : desensitizeFieldMapList) {
            // ① 从源数据库根据字段获取到的所有匹配的库表信息
            List<CdhColumnsEntity> cdhColumnsEntities = cdhMetadataService.queryTableByFieldName(desensitizeFieldMap.getDesensitizeField());
            // ② 过滤掉黑名单之中配置的字段
            cdhColumnsEntities = filterField(cdhColumnsEntities);
            // ③ 从现有脱敏字段列表中获取已配置的脱敏字段信息
            List<DesensitizeFieldList> desensitizeFieldLists = queryListNoPage(new DesensitizeFieldList().setFieldName(desensitizeFieldMap.getDesensitizeField()));
            // ④ 刷新脱敏字段
            resetDesensitizeFieldList(desensitizeFieldMap, cdhColumnsEntities, desensitizeFieldLists);
        }
    }

    /**
     * @Method filterField
     * @Param
     * @param cdhColumnsEntities
     * @Return java.util.List<com.hexinfo.dmpro.data.transfer.common.model.CdhColumnsEntity>
     * @Description 过滤掉黑名单中的字段
     * @Author Wang zhihao
     * @Date 2024/1/8 15:11
     * @Version V1.0
     */
    List<CdhColumnsEntity> filterField(List<CdhColumnsEntity> cdhColumnsEntities) {
        List<DesensitizeFieldFilter> filterLists = desensitizeFieldFilterService.queryListNoPage(new DesensitizeFieldFilter());
        List<CdhColumnsEntity> newList = cdhColumnsEntities.stream()
                .filter(cdhColumnsEntity -> filterLists.stream()
                        .noneMatch(filterList -> filterList.getDatabaseName().equals(cdhColumnsEntity.getDbName())
                                && filterList.getTableName().equals(cdhColumnsEntity.getTableName())
                                && filterList.getFieldName().equals(cdhColumnsEntity.getFieldName())))
                .collect(Collectors.toList());
        return newList;
    }

    /**
     * @Method resetDesensitizeFieldList
     * @Param
     * @param desensitizeFieldMap
     * @param cdhColumnsEntities
     * @Return void
     * @Description 刷新脱敏字段
     * @Author Wang zhihao
     * @Date 2024/1/5 14:06
     * @Version V1.0
     */
    private void resetDesensitizeFieldList(DesensitizeFieldMap desensitizeFieldMap, List<CdhColumnsEntity> cdhColumnsEntities, List<DesensitizeFieldList> desensitizeFieldLists) {
        // ① 获取需要新增的脱敏字段配置
        List<DesensitizeFieldList> addFieldList = cdhColumnsEntities.stream()
                // 过滤掉已存在的
                .filter(cdhColumnsEntity -> desensitizeFieldLists.stream()
                        .noneMatch(desensitizeFieldList -> desensitizeFieldList.getDbName().equals(cdhColumnsEntity.getDbName())
                                && desensitizeFieldList.getTableName().equals(cdhColumnsEntity.getTableName())
                                && desensitizeFieldList.getFieldName().equals(cdhColumnsEntity.getFieldName())))
                // 将有效信息重构为新的脱敏字段配置信息
                .map(cdhColumnsEntity -> new DesensitizeFieldList()
                        .setDbName(cdhColumnsEntity.getDbName())
                        .setTableName(cdhColumnsEntity.getTableName())
                        .setFieldName(cdhColumnsEntity.getFieldName())
                        .setFunctionId(desensitizeFieldMap.getFunctionId())
                        .setDesensitizeFunction(desensitizeFieldMap.getDesensitizeExample()))
                .collect(Collectors.toList());
        // ② 获取需要移除的脱敏字段配置
        List<DesensitizeFieldList> removeFieldList = desensitizeFieldLists.stream()
                // 过滤掉出不应该存在的
                .filter(desensitizeFieldList -> cdhColumnsEntities.stream()
                        .noneMatch(cdhColumnsEntity -> cdhColumnsEntity.getDbName().equals(desensitizeFieldList.getDbName())
                                && cdhColumnsEntity.getTableName().equals(desensitizeFieldList.getTableName())
                                && cdhColumnsEntity.getFieldName().equals(desensitizeFieldList.getFieldName())))
                // 重构为有效信息
                .map(desensitizeFieldList -> new DesensitizeFieldList()
                        .setDbName(desensitizeFieldList.getDbName())
                        .setTableName(desensitizeFieldList.getTableName())
                        .setFieldName(desensitizeFieldList.getFieldName()))
                .collect(Collectors.toList());

        // 去重
        addFieldList = CollectionUtil.distinct(addFieldList);
        removeFieldList = CollectionUtil.distinct(removeFieldList);
        // ③ 批量新增
        saveBatch(addFieldList);
        // ④ 批量删除
        removeBatch(removeFieldList);
    }

    /**
     * @Method removeBatch
     * @Param
     * @param removeFieldLists
     * @Return void
     * @Description 根据库、表、字段信息批量删除
     * @Author Wang zhihao
     * @Date 2024/1/5 15:46
     * @Version V1.0
     */
    private void removeBatch(List<DesensitizeFieldList> removeFieldLists) {
        for (DesensitizeFieldList removeFieldList : removeFieldLists) {
            LambdaQueryWrapper<DesensitizeFieldList> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(DesensitizeFieldList::getDbName, removeFieldList.getDbName());
            wrapper.eq(DesensitizeFieldList::getTableName, removeFieldList.getTableName());
            wrapper.eq(DesensitizeFieldList::getFieldName, removeFieldList.getFieldName());
            this.baseMapper.delete(wrapper);
        }
    }

    /**
     * @Method removeByFunctionIds
     * @Param functionIds
     * @Return boolean
     * @Description 根据脱敏函数ID删除脱敏字段配置表
     * @Author Yin.Yang
     * @Date 2023/12/28 14:14
     * @Version V1.0
     */
    @Override
    @Transactional
    public boolean removeByFunctionIds(List<String> functionIds) {
        LambdaQueryWrapper<DesensitizeFieldList> wrapper = Wrappers.lambdaQuery();
        wrapper.in(DesensitizeFieldList::getFunctionId, functionIds);
        return this.baseMapper.delete(wrapper) > 0;
    }

    /**
     * @Method removeByFunctionMapIds
     * @Param
     * @param functionMapIds
     * @Return boolean
     * @Description 根据脱敏函数映射ID删除脱敏字段配置表
     * @Author Wang zhihao
     * @Date 2024/1/4 17:28
     * @Version V1.0
     */
    @Override
    public boolean removeByFunctionMapIds(List<String> functionMapIds) {
        List<DesensitizeFieldMap> desensitizeFieldMapList = desensitizeFieldMapService.listByIds(functionMapIds);
        for (DesensitizeFieldMap desensitizeFieldMap : desensitizeFieldMapList) {
            LambdaQueryWrapper<DesensitizeFieldList> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(DesensitizeFieldList::getFunctionId, desensitizeFieldMap.getFunctionId());
            wrapper.eq(DesensitizeFieldList::getFieldName, desensitizeFieldMap.getDesensitizeField());
            this.baseMapper.delete(wrapper);
        }
        return true;
    }

    /**
     * @Method disabledByIds
     * @Param
     * @param ids
     * @Return void
     * @Description 批量根据ID禁用脱敏字段
     * @Author Wang zhihao
     * @Date 2024/1/15 16:54
     * @Version V1.0
     */
    @Override
    public void disabledByIds(List<String> ids) {
        for (String id : ids) {
            disableById(id);
        }
    }

    /**
     * @Method disableById
     * @Param
     * @param id
     * @Return void
     * @Description 根据ID禁用脱敏字段
     * @Author Wang zhihao
     * @Date 2024/1/15 16:55
     * @Version V1.0
     */
    @Transactional
    public void disableById(String id) {
        try {
            // 获取禁用字段的信息
            DesensitizeFieldList desensitizeFieldList = getById(id);
            DesensitizeFieldFilter desensitizeFieldFilter = new DesensitizeFieldFilter();
            desensitizeFieldFilter.setDatabaseName(desensitizeFieldList.getDbName());
            desensitizeFieldFilter.setTableName(desensitizeFieldList.getTableName());
            desensitizeFieldFilter.setFieldName(desensitizeFieldList.getFieldName());
            // 禁用字段的信息更新到黑名单中
            desensitizeFieldFilterService.save(desensitizeFieldFilter);
            // 删除该字段
            desensitizeFieldList.deleteById(id);
        } catch (Exception e) {
            throw new RuntimeException("根据ID禁用脱敏字段异常" + e.getMessage(), e);
        }
    }


    private boolean isExist(DesensitizeFieldList desensitizeFieldList, CdhColumnsEntity cdhColumnsEntity) {
        LambdaQueryWrapper<DesensitizeFieldList> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(DesensitizeFieldList::getDbName, cdhColumnsEntity.getDbName());
        wrapper.eq(DesensitizeFieldList::getTableName, cdhColumnsEntity.getTableName());
        wrapper.eq(DesensitizeFieldList::getFieldName, desensitizeFieldList.getFieldName());
        return this.baseMapper.selectCount(wrapper) > 0;
    }

    private void checkFile(MultipartFile mf) {
        if (mf.isEmpty()) {
            throw new RuntimeException("导入的文件为空文件");
        }
    }

    private File backupUploadFile(MultipartFile multipartFile) {
        return FileTransferUtil.saveFile(multipartFile, tempPath);
    }

    private List<FieldListDto> getExcelData(File file) {
        ExcelImportResult<FieldListDto> convertAfterList = readExcel(file);
        if (CollectionUtil.isNotEmpty(convertAfterList.getFailList())) {
            StringBuffer sb = new StringBuffer();
            for (FieldListDto p : convertAfterList.getFailList()) {
                sb.append("第" + (p.getRowNum() + 1) + "行的错误是：\n" + p.getErrorMsg().replaceAll(",", "\n")).append("\n");
            }
            throw new RuntimeException(sb.toString());
        }
        return convertAfterList.getList();
    }

    private ExcelImportResult<FieldListDto> readExcel(File file) {
        ImportParams importParams = new ImportParams();
        importParams.setNeedVerify(false);//开启校验
        importParams.setVerifyHandler(fieldListHandler); //自定义校验
        fieldListHandler.getThreadLocal().remove();
//        return ExcelImportUtil.importExcelMore(file, FieldListDto.class, importParams);
        ExcelImportResult<FieldListDto> a = ExcelImportUtil.importExcelMore(file, FieldListDto.class, importParams);
        System.out.println("");
        return a;
    }

    private void updateDbDataByExcel(List<FieldListDto> excelData) {
        for (FieldListDto importDto : excelData) {
            if (!importDto.isEmpty())
                save(importDto.toDesensitizeFieldList());
        }
    }
}
